python 生成器&迭代器
一、生成器
1、列表生成器:列表生成式就像是一个厨师,他只会做这n(n为任意整数)道菜,想吃甚麽做甚麽,不吃不做,不浪费空间;而列表表达式就相当于已经做好的n盘菜,占用空间。
2、生成器的创建方法:
- s = (x*2 for x in range(5))
- yield+数字 只要有yield就是一个生成器对象
注意:生成器在创建的时候已经决定了能计算出值得个数,调用next的个数一旦超过这个值就会报错
第一种:
s = (x*2 for x in range(5))
# 生成器的调用方法:next() print(next(s)) # 提取生成器里面的值,in python2中等价于 s._next_(),next() 一次打印一次
print(next(s))
print(next(s)) # 生成器就是一个可迭代对象(Iterable) for i in s: # 遍历生成器可迭代对象 print(i) # 打印完一个数之后这个数就被回收,不占用内存,只占用被引用值的哪一个内存
>>> 0 2 4 6 8
运行机制:从0开始,第一次next()时,取出一个数*2输出,然后每next()一次,取一个数,一直到2,for循环遍历时,因为前面next()已经取到了数字2的位置,所以从3开始,故输出为0 2 4 6 8
第二种:
def fun(): # 注意:生成器对象是fun(),而不是fun print('s') yield 1 print('ss') yield 2 g = fun() print(g) # <generator object fun at 0x000001D54E12A0F8> next(g) # 有返回值,为yield后的值 next(g)
解释:程序运行时先执行g = fun(), 执行到第一个next()时,返回到函数中执行具体内容,一直到执行完yield 1 停止,然后继续下一个next(),再到函数中寻找yield 2 的内容,以此类推。 注意:如果再多一个next(g),就会报错:StopIteration(停止迭代),因为超过了迭代次数
什么是可迭代对象:内部有 _iter_() 的都是可迭代对象(列表、元组、字典)
def fun(): print('s') yield 1 print('ss') yield 2 for i in fun(): print(i) # yield 有返回值,返回值赋给了i
>>>
s
1
ss
2
例1:输出斐波那契数列
def fib(maxs): n, before, after = 0, 0, 1 while n < maxs: print(after) # 从1开始,如打印before则从从0开始 before, after = after, before+after # 先计算后赋值(先右后左) n = n+1 fib(10) # 输出前10个斐波那契数
注意:before, after = after, before+after 和 下面的两条语句不等价,原因见例1对应标注;
before = after
after = before+after
例2:斐波那契数列生成器
def fib(maxs): n, before, after = 0, 0, 1 while n < maxs: yield before before, after = after, before+after # 先计算后赋值(先右后左) n = n+1 g = fib(8) print(g) # 生成器所在的内存地址 # 需要几个next() 几次,但最多不能超过8次,否则应修改fib() 里面的数字 print(next(g)) print(next(g)) print(next(g)) print(next(g))
3、 .send() 方法
def fun(): # fun()是一个生成器对象而不是fun print('qwer') count = yield 1 # 先把yield的值返回,然后把‘xss’赋值给变量count print(count) print('asdf') yield 2 f = fun() # next(f) # 进入函数体 f.send(None) # 等价于next(f)
s = f.send('xss') # 给yield前面的变量赋值 print(s)# s 是yield 2 的返回值
注意:第一次send前如果没有next,只能传一个send(None),否则报错,TypeError: can't send
non-None value to a just-started generator,第一次通过进入send(None) 进入函数之后首先执行
print(),yield返回一个1,再次进入之后就把send()里面的值传给count,然后就可以对count进行操作
例3:yield 伪并发的实现
import time def consumer(name): print("%s准备吃包子!" % name) while True: baozi = yield print("包子[%s]来了,被[%s]吃了!" % (baozi, name)) def producer(name): a1 = consumer('A') a2 = consumer('B') next(a1) next(a2) print("%s准备吃包子了!" % name) for i in range(5): time.sleep(1) print("做了2个包子") a1.send(i) a2.send(i) producer("xiaoss")
>>>
A准备吃包子!
B准备吃包子!
xiaoss准备吃包子了!
做了2个包子
包子[0]来了,被[A]吃了!
包子[0]来了,被[B]吃了!
做了2个包子
包子[1]来了,被[A]吃了!
包子[1]来了,被[B]吃了!
做了2个包子
包子[2]来了,被[A]吃了!
包子[2]来了,被[B]吃了!
做了2个包子
包子[3]来了,被[A]吃了!
包子[3]来了,被[B]吃了!
做了2个包子
包子[4]来了,被[A]吃了!
包子[4]来了,被[B]吃了!
二、迭代器
1、什么是迭代器?
迭代是指可以用for循环来遍历
for循环内部三件事: 1.调用可跌代对象的iter方法返回一个可迭代对象
2.不断调用可迭代对象的next方法
3.处理StopIteration
处理StopIteration的过程如下:
while: try: I = next(list_Iterator) except StopIteration: break
注:生成器都是迭代器,迭代器不一定是生成器
2、满足两个条件: (1)有iter方法 (2) 有next方法
from collections import Iterable,Iterator # 调用Iterable和Iterator lis = [1, 2, 3, 4] d = iter(lis) # 相当于 l._iter_() print(d) # <list_iterator object at 0x00000206700DC358> print(next(d)) print(next(d)) print(next(d)) print(next(d)) for i in [1, 2, 3, 4]: iter([1, 2, 3, 4]) print(isinstance([1, 2], list)) # 判断一个字符是不是某个特定的数据类型 isinstance(数据, 数据类型) print(isinstance(lis, Iterable)) # 判断lis是不是迭代器
>>>
<list_iterator object at 0x000002756B321240>
1
2
3
4
True
True
例1:用迭代找出文档中最长的行
F = max(len(x.strip()) for x in open('file_1', 'r')) # 文件读取,找出最长的行 print(F) # 最长行字符的个数